package moa.evaluation;

import java.util.ArrayList;
import moa.cluster.Clustering;
import moa.gui.visualization.DataPoint;


public class EntropyCollection extends MeasureCollection{
	private static final long serialVersionUID = 1L;
	private boolean debug = false;
	private final double beta = 1;


	@Override
	protected String[] getNames() {
		String[] names = {"GT cross entropy","FC cross entropy","Homogeneity","Completeness","V-Measure","VarInformation"};
		return names;
	}

	@Override
	public void evaluateClustering(Clustering fclustering, Clustering hClustering, ArrayList<DataPoint> points) throws Exception {

		MembershipMatrix mm = new MembershipMatrix(fclustering, points);
		int numClasses = mm.getNumClasses();
		int numCluster = fclustering.size()+1;
		int n = mm.getTotalEntries();


		double FCentropy = 0;
		if(numCluster > 1){
			for (int fc = 0; fc < numCluster; fc++){
				double weight = mm.getClusterSum(fc)/(double)n;
				if(weight > 0)
					FCentropy+= weight * Math.log10(weight);
			}
			FCentropy/=(-1*Math.log10(numCluster));
		}
		if(debug){
			System.out.println("FC entropy "+FCentropy);
		}

		double GTentropy = 0;
		if(numClasses > 1){
			for (int hc = 0; hc < numClasses; hc++){
				double weight = mm.getClassSum(hc)/(double)n;
				if(weight > 0)
					GTentropy+= weight * Math.log10(weight);
			}
			GTentropy/=(-1*Math.log10(numClasses));
		}
		if(debug){
			System.out.println("GT entropy "+GTentropy);
		}


		//cluster based entropy
		double FCcrossEntropy = 0;

		for (int fc = 0; fc < numCluster; fc++){
			double e = 0;
			int clusterWeight = mm.getClusterSum(fc);
			if(clusterWeight>0){
				for (int hc = 0; hc < numClasses; hc++) {
					double p = mm.getClusterClassWeight(fc, hc)/(double)clusterWeight;
					if(p!=0){
						e+=p * Math.log10(p);
					}
				}
				FCcrossEntropy+=((clusterWeight/(double)n) * e);
			}
		}
		if(numCluster > 1){
			FCcrossEntropy/=-1*Math.log10(numCluster);
		}

		addValue("FC cross entropy", 1-FCcrossEntropy);
		if(debug){
			System.out.println("FC cross entropy "+(1-FCcrossEntropy));
		}


		//class based entropy
		double GTcrossEntropy = 0;
		for (int hc = 0; hc < numClasses; hc++){
			double e = 0;
			int classWeight = mm.getClassSum(hc);
			if(classWeight>0){
				for (int fc = 0; fc < numCluster; fc++) {
					double p = mm.getClusterClassWeight(fc, hc)/(double)classWeight;
					if(p!=0){
						e+=p * Math.log10(p);
					}
				}
			}
			GTcrossEntropy+=((classWeight/(double)n) * e);
		}
		if(numClasses > 1)
			GTcrossEntropy/=-1*Math.log10(numClasses);
		addValue("GT cross entropy", 1-GTcrossEntropy);
		if(debug){
			System.out.println("GT cross entropy "+(1-GTcrossEntropy));
		}

		double homogeneity;
		if(FCentropy == 0)
			homogeneity = 1;
		else
			homogeneity = 1 - FCcrossEntropy/FCentropy;
		//set err values for now, needs to be debugged
		if(homogeneity > 1 || homogeneity < 0)
			addValue("Homogeneity",-1);
		else
			addValue("Homogeneity",homogeneity);

		double completeness;
		if(GTentropy == 0)
			completeness = 1;
		else
			completeness = 1 - GTcrossEntropy/GTentropy;
		addValue("Completeness",completeness);

		double vmeasure = (1+beta)*homogeneity*completeness/(beta*homogeneity+completeness);
		if(Double.isNaN(vmeasure)){

		}

		if(vmeasure > 1 || homogeneity < 0)
			addValue("V-Measure",-1);
		else
			addValue("V-Measure",vmeasure);



		double mutual = 0;
		for (int i = 0; i < numCluster; i++){
			for (int j = 0; j < numClasses; j++) {
				if(mm.getClusterClassWeight(i, j)==0) continue;
				double m = Math.log10(mm.getClusterClassWeight(i, j)/(double)mm.getClusterSum(i)/(double)mm.getClassSum(j)*(double)n);
				m*= mm.getClusterClassWeight(i, j)/(double)n;
				if(debug)
					System.out.println("("+j+"/"+ j + "): "+m);
				mutual+=m;
			}
		}
		if(numClasses > 1)
			mutual/=Math.log10(numClasses);

		//        if(Math.abs(mutualraw + varInfoFC + varInfoHC) < 1e-10)
		//            varInfo = 1;
		//        else
		//            varInfo = 2*mutualraw/(-varInfoFC - varInfoHC );
		double varInfo = 1;
		if(FCentropy + GTentropy > 0)
			varInfo = 2*mutual/(FCentropy + GTentropy);

		if(debug)
			System.out.println("mutual "+mutual+ " / VI "+varInfo);
		addValue("VarInformation", varInfo);

	}

}
